--[[
    This code is responsible for managing the overall layout of the timers for any specific module.  
    Basically, it deals with what happens when a timer is added or removed - the actual updates of 
    the timer are left for other code.
    
    Timer Scripts:
        OnTimerEnter(frame, timerdata)
        OnTimerClick(frame, timerdata, button)
        OnGroupEnter(frame, groupdata)
        OnGroupClick(frame, groupdata, button)
        OnDragEnter(frame)
        OnDragClick(frame, button)
--]]

TimerLib = AsheylaLib:NewModule("TimerLib");

local GetTime = GetTime;
local huge = math.huge;
local lower = string.lower;

local timerModule = TimerLibTimer;
local anchorModule = TimerLibAnchor;

TimerLib.anchors = {};
local anchors = TimerLib.anchors;

local defaultSettings, updateTimerSettings;

function updateTimerSettings(self)
    if not self:GetUserSet("timerSettings", "Default Timer") then
        self:Set("timerSettings", "Default Timer", {});
    end
    if not self:GetUserSet("anchorSettings", "Default Anchor") then
        self:Set("anchorSettings", "Default Anchor", {});
    end
    
    for name, anchor in pairs(anchors) do
        anchor:UpdateSettings();
    end
end

function TimerLib:Commands(msg)
    local loaded = LoadAddOn("DoTimer_Options");
    if loaded then
        local panel = (msg == "a" or msg == "anchor") and "DoTimer Anchors" or "DoTimer Timers";
        GUILib:ShowPanel(panel);
    end
end
TimerLib:MakeSlashCmd("/timerlib", "/tl");

-- called by external modules to create timers onscreen.  TimerLib assumes that timerdata is a table consisting of the following elements:
--[[
    name: name of the timer
    time: (optional) when it began
    duration (optional): how long it's running
    texture (optional): texture of the timer
    stack (optional): stack of the timer
--]]
-- groupdata is as follows:
--[[
    name: group name
    icon (optional): raid icon
    level (optional): target level
    uniqueID (optional): a unique identifier for the group
--]]
-- groupdata can also be a string representing the name of the group, if you don't want to make a table for it

function TimerLib:NewTimer(timerdata, groupdata, module, timerSettings, timerPriority, timerHidden, anchorName, groupPriority, groupHidden)
    module = module or self;
    timerdata = timerdata or {duration = 10, time = GetTime()};
    groupdata = groupdata or "Default Group";
    anchorName = anchorName or "Default Anchor";
    local timer = timerModule:NewTimer(module, timerdata, timerSettings, timerPriority, timerHidden);
    local anchor = anchors[anchorName] or anchorModule:NewAnchor(anchorName);
    anchors[anchorName] = anchor;
    local group = anchor:AddTimer(timer, groupdata, groupPriority, groupHidden);
    return timer, group;
end

-- flags the timer for updating on the screen (something about it has changed)
function TimerLib:UpdateTimer(timer, timerdata)
    if not timer then return false; end
    if not timerModule:IsActiveTimer(timer) then return false; end
    if timer.data ~= timerdata then return false; end
    timer:UpdateData();
    timer.group:SortTimers();
    return timer;
end

-- flags the group for updating on the screen (something about it has changed)
function TimerLib:UpdateGroup(group, groupdata)
    if not group then return; end
    if group.data ~= groupdata then return; end
    group:UpdateData();
end

-- deletes the timer from the screen
function TimerLib:DeleteTimer(timer, timerdata, destroy)
    if not timer then return; end
    if timer.data ~= timerdata then return; end
    if destroy then
        timer.group:DestroyTimer(timer);
    else
        timer.group:RemoveTimer(timer);
    end
end

function TimerLib:UpdateGroupInfo(group, groupdata, priority, hidden)
    if not group then return; end
    if group.data ~= groupdata then return; end
    local changed = false;
    if group.priority ~= priority then
        group.priority = priority;
        changed = true;
        group.index = nil; -- to force resort
    end
    if group.hidden ~= hidden then
        group.hidden = hidden;
        changed = true;
        group.index = nil; -- to force resort
    end
    if changed then group.anchor:SortGroups(); end
end

function TimerLib:UpdateTimerInfo(timer, timerdata, priority, hidden, settings)
    if not timer then return false; end
    if not timerModule:IsActiveTimer(timer) then return false; end
    if timer.data ~= timerdata then return false; end
    local changed = false;
    if timer.priority ~= priority then
        timer.priority = priority;
        changed = true;
        timer.index = nil; -- to force resort
    end
    if timer.hidden ~= hidden then
        timer.hidden = hidden;
        changed = true;
        timer.index = nil; -- to force resort
    end
    if timer.settings ~= settings then
        timer.settings = settings;
        changed = true;
        timer:DrawFrame();
    end
    if changed then
        timer.group:Resize(timer.group:SortTimers());
        timer.group.index = nil;
        timer.group.anchor:SortGroups();
    end
    return timer;
end

function TimerLib:MoveGroup(group, groupdata, anchorName)
    if not group then return; end
    if group.data ~= groupdata then return; end
    if type(groupdata) ~= "table" then return; end
    anchorName = anchorName or "Default Anchor";
    local newAnchor = anchors[anchorName] or anchorModule:NewAnchor(anchorName);
    anchors[anchorName] = newAnchor;
    if newAnchor == group.anchor then return; end
    group.anchor:RemoveGroup(group);
    newAnchor:AddGroup(group);
end

function TimerLib:MoveTimer(timer, timerdata, groupdata, anchorName, groupPriority, groupHidden)
    if not timer then return; end
    if timer.data ~= timerdata then return; end
    --if type(groupdata) ~= "string" then return; end
    --if type(timer.group.data) ~= "string" then return; end
    return timer.group:MoveTimer(timer, group, groupdata, anchorName, groupPriority, groupHidden);
end

function TimerLib:DeleteAllTimers(module)
    for name, anchor in pairs(anchors) do
        for i = #anchor.groups, 1, -1 do
            local group = anchor.groups[i];
            for timer in group:GetTimers() do
                if timer.module == module then
                    group:DestroyTimer(timer);
                end
            end
        end
    end
end

function TimerLib:SetAnchorPositionUpdate(anchorName, bool)
    for name, anchor in pairs(anchors) do
        if name == anchorName then
            anchor.canUpdatePosition = bool;
        end
    end
end

TimerLib.stateMeta = {
    __index = function(t, k)
        return t.state[k];
    end,
    
    __newindex = function(t, k, v)
        t.state[k] = v;
    end,
}

TimerLib.findFrameElement = function(frame, element)
    if not frame[element] then
        frame[element] = getglobal(frame:GetName() .. element);
    end
    
    return frame[element];
end

local ttnmatches = {
	["s"] = 1,
	["m"] = 60,
	["h"] = 3600,
	["d"] = 86400,
}
function TimerLib.textToNum(text)
	local total = tonumber(text) or 0;
    for num, letter in string.gmatch(text, "(%d+)([smhd])") do
        total = total + (num * ttnmatches[letter]);
    end
    return total;
end

local nttmatches = {
	{len = 86400,letter = "d"},
	{len = 3600,letter = "h"},
	{len = 60,letter = "m"},
	{len = 1,letter = "s"},
}
function TimerLib.numToText(num)
    if num == 0 then return "0s"; end
	local text = "";
    for _, info in ipairs(nttmatches) do
        local n = math.floor(num / info.len);
        if n > 0 then
            text = text .. n .. info.letter .. " ";
            num = num - (n * info.len);
        end
    end
    return text;
end

defaultSettings = {
    anchorSettings = {
        ["Default Anchor"] = {
            locked = false,
            displayNames = true,
            displayIcons = true,
            groupDirection = "down",
            groupSpacing = 10,
            timerDirection = "down", 
            timerSpacing = 5, 
            overflowDirection = "right",
            moveName = true,
            scale = 1,
            timerSortMethod = "Time Remaining (D)",
            groupSortMethod = "Time Added (D)",
            maxNumGroups = 0,
            overflowPoint = 0,
            defaultTimerSetting = "Default Timer",
            standardAlpha = 1,
            combatAlpha = 1,
            mouseoverAlpha = 1,
            hideAll = false,
            nameFontHeight = 12,
            timerJustification = "auto",
            centering = "none",
            anchorsToCursor = false, -- not customizable
            nameTextColor = {
                r = 1.0, g = .82, b = 0,
            },
        },
    },
    
    timerSettings = {
        ["Default Timer"] = {
            timerFormat = "bar",
            alpha = .75,
            scale = 1,
            ghostLength = 3;
            redPoint = 5,
            fadeInLength = 1,
            fadeOutLength = 1,
            timeFormat = "letters",
            tenths = true,
            clickable = true,
            soundAlert = false,
            ghosts = true,
            tooltips = true,
            
            iconDisplayFormat = "outside",
            iconShowCooldown = false,
            iconFlipText = false,
            iconReverseCooldown = true,
            iconFontHeight = 12,
            iconUseBG = false;
            
            barFlipIcon = false,
            barMajorLength = 150,
            barMinorLength = 16,
            barFontHeight = 12,
            barOrientation = "horizontal",
            barShowIcon = true,
            barTexture = "Aluminium",
            barTextFormat = "%s&:%d",
            barReversed = false,
            barGrow = false,
            barScaling = "standard",
            barAbsScale = 0,
            barTextColor = {
                r = .95, g = .95, b = .95,
            },
            barBackgroundColor = { -- now also applied to icons, but keeping the name so the setting doesn't break
                r = .1, g = .6, b = .8,
            },
            
            startColor = {
                r = .2, g = .9, b = .2,
            },
            middleColor = {
                r = .9, g = .9, b = .2,
            },
            finalColor = {
                r = .9, g = .2, b = .2,
            },
            ghostColor = {
                r = .32, g = .56, b = 1.0,
            },
        },
    },
};

TimerLib:AddDefaultSettings(defaultSettings);
TimerLib:AddSettingsUpdateScript(updateTimerSettings);
